
#include "func.h"
#include <cstddef>
#include <time.h>

#include <map>
#include <utility>

using std::map;

#define MAX_EVENT_SIZE 100
#define MAX_TIMEOUT 1000


int main()
{
    //1.socket
    int listenfd = socket(AF_INET,SOCK_STREAM,0);
    if(listenfd < 0){
        perror("socket");
        return EXIT_FAILURE;
    }

    int on = 1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

    //2.bind
    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(12345);
    serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    if(ret < 0){
        perror("bind");
        close(listenfd);
        return EXIT_FAILURE;
    }

    //3.listen
    ret = listen(listenfd,10); //允许同时处理10个客户连接 
    if(ret < 0){
        perror("listen");
        close(listenfd);
        return EXIT_FAILURE;
    }

    //创建epoll
    int epfd = epoll_create1(0);
    if(epfd < 0){
        perror("epoll_create1");
        close(listenfd);
        return EXIT_FAILURE;
    }

    //增加对listenfd的监听
    struct epoll_event ev;
    memset(&ev,0,sizeof(ev));
    ev.data.fd = listenfd;
    ev.events = EPOLLIN;
    ret = epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
    if(ret < 0){
        perror("epoll_ctl");
        close(listenfd);
        close(epfd);
        return EXIT_FAILURE;
    }

    map<int,int> client_fds; //存放所有客户端的fd,上次活跃时间
    struct epoll_event evArr[MAX_EVENT_SIZE] = {0};
    while(1){
        int nready = epoll_wait(epfd,evArr,MAX_EVENT_SIZE,MAX_TIMEOUT);
        if(nready == -1 && errno == EINTR){ //eintr
            continue; //被信号影响
        }else if(nready == -1){
            perror("epoll_wait");
            return EXIT_FAILURE;
        }else if(0 == nready){
            //printf("epoll timeout \n");
        }else{
            //有文件描述符就绪
            for(int i = 0; i< nready;++i){
                int fd = evArr[i].data.fd;
                if(fd == listenfd){
                    //有新客户连接
                    struct sockaddr_in clientaddr;
                    socklen_t addrlen = sizeof(clientaddr);
                    memset(&clientaddr,0,sizeof(clientaddr));
                    int netfd = accept(listenfd,(struct sockaddr*)&clientaddr,&addrlen);
                    printf("netfd:%d, client info: %s:%d, current_time:%ld \n"
                           ,netfd
                           ,inet_ntoa(clientaddr.sin_addr)
                           ,ntohs(clientaddr.sin_port)
                           ,time(NULL));
                    //将新连接放入epoll
                    ev.data.fd = netfd;
                    ev.events = EPOLLIN;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,netfd,&ev);
                    client_fds[netfd] = time(NULL);
                }else{
                    char buf[1024] = {0};
                    ret = recv(fd,buf,sizeof(buf),0);
                    if(ret == 0){
                        printf("客户端fd = %d 断开连接\n",fd);
                        close(fd);
                        ev.data.fd = fd;
                        epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev);
                        client_fds.erase(fd);
                        continue;
                    }
                    printf("recv %d bytes, buf: %s \n",ret,buf);
                    //更新当前客户端的活跃时间
                    client_fds[fd] = time(NULL);
                    //转发给其他所有客户端
                    for(auto it = client_fds.begin(); it != client_fds.end();++it){
                        if(it->first != fd){ //发给除了当前客户的其他客户
                            ret = send(it->first,buf,strlen(buf),0);
                        }
                    }
                    printf("send %d bytes \n",ret);

                }
            }
        }
        //踢出超时用户
        int current_time = time(NULL);
        /* printf("超时检查的时间：%d\n",current_time); */
        auto it = client_fds.begin();
        while(it != client_fds.end()){

            if(current_time - client_fds[it->first] > 20){
                printf("客户端fd = %d 超时踢出,current_time:%d\n",it->first,current_time);
                close(it->first);
                ev.data.fd = it->first;
                epoll_ctl(epfd,EPOLL_CTL_DEL,it->first,&ev);
                //删除map中信息
                it = client_fds.erase(it);
            }else{
                ++it;
            }     
        }
    }
    return 0;
}


